// Based on PackageDigitalSignature SDK Sample - PackageDigitalSignature.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace DigitalSignature
{
    internal class PackageDigitalSignature
    {
        public static void Main(string[] argv)
        {
            if (argv.Length < 3)
            {
                Console.WriteLine("PackageFilename CertificateFilename Password");
                return;
            }
            var targetFilename = argv[0];
            var certFilename = argv[1];
            var password = argv[2];
            var certificate = new X509Certificate(certFilename, password);

            // Open the Package copy in the target directory.
            using (var package = Package.Open(targetFilename, FileMode.Open, FileAccess.ReadWrite))
            {
                if (ValidateSignatures(package))
                {
                    Console.WriteLine("Already signed");
                    return;
                }

                SignAllParts(package, certificate);
                // Validate the Package Signatures.
                // If all package signatures are valid, go ahead and unpack.
                if (ValidateSignatures(package))
                {
                    Console.WriteLine("OK");
                }
            }
        }

        /// <summary>
        ///   Validates all the digital signatures of a given package.</summary>
        /// <param name="package">
        ///   The package for validating digital signatures.</param>
        /// <returns>
        ///   true if all digital signatures are valid; otherwise false if the
        ///   package is unsigned or any of the signatures are invalid.</returns>
        private static bool ValidateSignatures(Package package)
        {
            if (package == null)
                throw new ArgumentNullException("package");

            // Create a PackageDigitalSignatureManager for the given Package.
            var dsm = new PackageDigitalSignatureManager(package);

            // Check to see if the package contains any signatures.
            if (!dsm.IsSigned)
                return false; // The package is not signed.

            // Verify that all signatures are valid.
            var result = dsm.VerifySignatures(false);
            return result == VerifyResult.Success;
        }

        private static void SignAllParts(Package package, X509Certificate certificate)
        {
            if (package == null)
                throw new ArgumentNullException("package");
            if (certificate == null)
                throw new ArgumentNullException("certificate");

            // Create the DigitalSignature Manager
            var dsm = new PackageDigitalSignatureManager(package)
            {
                CertificateOption = CertificateEmbeddingOption.InSignaturePart
            };

            // Create a list of all the part URIs in the package to sign
            // (GetParts() also includes PackageRelationship parts).
            var toSign = new List<Uri>();
            foreach (var packagePart in package.GetParts())
            {
                // Add all package parts to the list for signing.
                toSign.Add(packagePart.Uri);
            }

            // Add the URI for SignatureOrigin PackageRelationship part.
            // The SignatureOrigin relationship is created when Sign() is called.
            // Signing the SignatureOrigin relationship disables counter-signatures.
            toSign.Add(PackUriHelper.GetRelationshipPartUri(dsm.SignatureOrigin));

            // Also sign the SignatureOrigin part.
            toSign.Add(dsm.SignatureOrigin);

            // Add the package relationship to the signature origin to be signed.
            toSign.Add(PackUriHelper.GetRelationshipPartUri(new Uri("/", UriKind.RelativeOrAbsolute)));

            try
            {
                dsm.Sign(toSign, certificate);
                Console.WriteLine("Signed");
            }
            catch (CryptographicException ex)
            {
                Console.WriteLine("Cannot Sign\n" + ex);
            }
        }
    }
}